<script lang="ts" setup>
import type {
  UploadRequestOptions,
  UploadUserFile,
} from 'element-plus'
import OSS from 'ali-oss'
import axios from 'axios'
import { ref } from 'vue'

interface Credentials {
  AccessKeyId: string
  AccessKeySecret: string
  SecurityToken: string
  Expiration: string
}

const credentials = ref<Credentials | null>() // STS凭证
let ossClient: any = null // oss客户端实例
const bucket = 'haw-gc' // bucket名称
const region = 'oss-cn-guangzhou' // oss服务区域名称
const partSize = 1024 * 1024 // 每个分片大小(byte)
const parallel = 3 // 同时上传的分片数
const checkpoints: any = {} // 所有分片上传文件的检查点
// 获取STS Token
async function getCredential() {
  const res: { data: Credentials } = await axios.get(
    'http://127.0.0.1:3007/sts',
  )
  credentials.value = res.data

  console.log('credentials', credentials)
}

// 创建 OSS 实例
async function initOSSClient() {
  ossClient = new OSS({
    accessKeyId: credentials.value?.AccessKeyId,
    accessKeySecret: credentials.value?.AccessKeySecret,
    stsToken: credentials.value?.SecurityToken,
    bucket,
    region,
  })
}

const fileList = ref<UploadUserFile[]>([])

/**
 * 自定义上传文件
 * @param options 上传参数
 */
async function fnUploadRequest(options: UploadRequestOptions) {
  await getCredential()
  const file = options.file
  // 如果文件大于 100m 分片
  if (file.size > 50 * 1024 * 1024) {
    multipartUpload(file)
  }
  else {
    commonUpload(file)
  }
}

// 普通上传
async function commonUpload(file: File) {
  if (!ossClient) {
    await initOSSClient()
  }

  const fileName = file.name
  ossClient
    .put(fileName, file)
    .then((res: any) => {
      if (res.res.status === 200) {
        console.log('上传成功', res)
      }
      else {
        console.log('上传失败', res)
      }
    })
    .catch((err: any) => {
      console.log('上传失败', err)
    })
}

// 分片上传
async function multipartUpload(file: File) {
  if (!ossClient) {
    await initOSSClient()
  }

  const fileName = file.name
  ossClient
    .multipartUpload(fileName, file, {
      parallel,
      partSize,
      progress: onMultipartUploadProgress,
    })
    .then((res: any) => {
      console.log('上传成功', res)
    })
    .catch((err: any) => {
      console.log('上传失败', err)
    })
}

// 分片上传进度
function onMultipartUploadProgress(progress: number, checkpoint: { uploadId: string, name: string, file: File }) {
  console.log(`${checkpoint.name}上传进度为： ${progress}`)
  checkpoints[checkpoint.uploadId] = checkpoint
  // 判断STS Token 是否将要过期，过期则重新获取
  // ...
}

// 断点续传
function resumeMultipartUpload() {
  Object.values(checkpoints).forEach((checkpoint: any) => {
    console.log(checkpoint)
    const { uploadId, file, name } = checkpoint
    ossClient
      .multipartUpload(name, file, {
        partSize,
        parallel,
        progress: onMultipartUploadProgress,
        checkpoint,
      })
      .then((res: any) => {
        console.log('断点续传上传成功', res)
        delete checkpoints[uploadId]
      })
      .catch((err: any) => {
        console.log('断点续传上传失败', err)
      })
  })
}

function stop() {
  ossClient.cancel()
  console.log('暂停---------------------------------------')
}
function resume() {
  resumeMultipartUpload()
}
</script>

<template>
  <div class="box">
    <el-upload
      v-model:file-list="fileList"
      class="upload-demo"
      action=""
      :http-request="fnUploadRequest"
      multiple
      :limit="1"
    >
      <el-button type="primary">
        Click to upload
      </el-button>
      <template #tip>
        <div class="el-upload__tip">
          jpg/png files with a size less than 500KB.
        </div>
      </template>
    </el-upload>

    <el-button type="danger" @click="stop">
      暂停
    </el-button>
    <el-button type="success" @click="resume">
      继续
    </el-button>
  </div>
</template>

<style lang="scss">
.box {
  margin: 50px auto;
  width: 500px;
  height: 300px;
}
</style>
